<?php

/**
 * Current Http component of request
 */
class Request
{

	/**
	 * main request that instance current process
	 * @var Request
	 */
	static protected $_initiator = null;

	/**
	 * get main request
	 * @return Request
	 */
	static public function initiator()
	{
		if (null == self::$_initiator)
		{
			if (php_sapi_name() == 'cli')
			{
				self::$_initiator = new Request_Cli;
			}
			else
			{
				self::$_initiator = new Request_Http;
			}
		}
		return self::$_initiator;
	}

	/**
	 * Retrieve a member of the $_COOKIE superglobal
	 *
	 * If no $key is passed, returns the entire $_COOKIE array.
	 *
	 * @todo How to retrieve from nested arrays
	 * @param string $key
	 * @param mixed $default Default value to use if key not found
	 * @return mixed Returns null if key does not exist
	 */
	static public function getCookie($key = null, $default = null)
	{
		return null === $key ? $_COOKIE : (isset($_COOKIE[$key]) ? $_COOKIE[$key] : $default);
	}

	/**

	 * @param string $key
	 * @param string $default
	 * @return string
	 */
	static public function getServer($key = null, $default = null)
	{
		return null === $key ? $_SERVER : (isset($_SERVER[$key])) ? $_SERVER[$key] : $default;
	}

	/**
	 * @param string $key
	 * @param string $default
	 * @return string
	 */
	static public function getEnv($key = null, $default = null)
	{
		return null === $key ? $_ENV : (isset($_ENV[$key])) ? $_ENV[$key] : $default;
	}

	/**
	 * @param string $key
	 * @return string
	 */
	static public function getHeader($key)
	{

		// Try to get it from the $_SERVER array first
		$temp = 'HTTP_' . strtoupper(str_replace('-', '_', $key));

		if (isset($_SERVER[$temp]))
		{
			return $_SERVER[$temp];
		}

		if (function_exists('apache_request_headers'))
		{
			$headers = apache_request_headers();
			if (isset($headers[$key]))
			{
				return $headers[$key];
			}
			$header = strtolower($key);
			foreach ($headers as $k => $v)
			{
				if (strtolower($k) == $key)
				{
					return $value;
				}
			}
		}

		return false;
	}

	/**
	 * @return string
	 */
	static public function getScheme()
	{
		return self::getServer('HTTPS') == 'on' ? 'https' : 'http';
	}

	/**
	 * Get the HTTP host.
	 *
	 * "Host" ":" host [ ":" port ] ; Section 3.2.2
	 * Note the HTTP Host header is not the same as the URI host.
	 * It includes the port while the URI host doesn't.
	 *
	 * @return string
	 */
	static public function getHttpHost()
	{
		$host = self::getServer('HTTP_HOST');

		if (!empty($host))
		{
			return $host;
		}

		$scheme = self::getScheme();
		$name = self::getServer('SERVER_NAME');
		$port = self::getServer('SERVER_PORT');

		if (null === $name)
		{
			return '';
		}
		elseif (($scheme == 'http' && $port == 80) || ($scheme == 'https' && $port == 443))
		{
			return $name;
		}
		else
		{
			return $name . ':' . $port;
		}
	}

	/**
	 * @return string
	 */
	static public function getClientIp()
	{
		if (self::getServer('HTTP_CLIENT_IP'))
		{
			self::getServer('HTTP_CLIENT_IP');
		}

		if (self::getServer('HTTP_X_FORWARDED_FOR'))
		{
			return self::getServer('HTTP_X_FORWARDED_FOR');
		}

		return self::getServer('REMOTE_ADDR', '127.0.0.1');
	}

	/**
	 *
	 * @return boolean
	 */
	static public function isFlash()
	{
		return strpos(self::getHeader('USER_AGENT'), ' flash') !== false;
	}

	/**
	 * Is the request a Javascript XMLHttpRequest?
	 *
	 * Should work with Prototype/Script.aculo.us, possibly others.
	 *
	 * @return boolean
	 */
	static public function isAjax()
	{
		return (self::getHeader('X_REQUESTED_WITH') == 'XMLHttpRequest');
	}

	/**
	 * target format: path/to/controller/action
	 * @var string
	 */
	protected $_controller = null;

	/**
	 * @var array
	 */
	protected $_params = array();

	/**
	 * main object that touch by this request, it's helful.
	 */
	protected $_subject = null;

	/**
	 * response body
	 */
	public $response = '';

	/**
	 * constructor
	 * @return void
	 */
	public function __construct($controller = null, $params = null)
	{
		$this -> _controller = $controller;
		$params && $this -> setParams($params);
	}

	/**
	 * magic get method
	 * please do not get this method to fetch from
	 * 'subject','response','target','method','params', it's drangourous
	 * @param string $name
	 * @return mixed
	 */
	public function __get($key)
	{
		return isset($this -> _params[$key]) ? $this -> _params[$key] : null;
	}

	/**
	 * magic setter method
	 * @param   string  $key
	 * @param   mixed   $value
	 * @return void
	 */
	public function __set($key, $value)
	{
		$this -> _params[$key] = $value;
	}

	/**
	 * @return string
	 */
	public function __toString()
	{
		return $this -> response;
	}

	/**
	 * set target path
	 * @param string $controller
	 * @return Request
	 */
	public function setController($controller)
	{
		$this -> _controller = $controller;
		return $this;
	}

	/**
	 * get current target
	 * if target is null, disptacher will be called to mersure the target
	 *
	 * @return string
	 */
	public function getController()
	{
		if (null === $this -> _controller)
		{
			Router::dispatch($this);
		}
		return $this -> _controller;
	}

	/**
	 * set subject of current request
	 * @param any $subject
	 * @return Request
	 */
	public function setSubject($subject)
	{
		$this -> _subject = $subject;
		return $this;
	}

	/**
	 * return subject data
	 * @return object/array/null
	 */
	public function getSubject()
	{
		return $this -> _subject;
	}

	/**
	 * set parametter
	 * @param array $array
	 * @return Request
	 */
	public function setParams($array)
	{
		foreach ($array as $key => $value)
		{
			$this -> _params[$key] = $value;
		}
		return $this;
	}

	/**
	 * @return array
	 */
	public function getParams()
	{
		return $this -> _params;
	}

	/**
	 * set param
	 * @param string $key
	 * @param mixed  $value
	 * @return Request
	 */
	public function setParam($key, $value)
	{
		$this -> _params[$key] = $value;
		return $this;
	}

	/**
	 * @param string $key
	 * @param mixed $default_value
	 * @return Request
	 */
	public function getParam($key, $default_value = null)
	{
		return isset($this -> _params[$key]) ? $this -> _params[$key] : $default_value;
	}

	/**
	 * get data
	 * @return array
	 */
	public function getGet()
	{
		return $this -> _params;
	}

	/**
	 * post data
	 * @return array
	 */
	public function getPost()
	{
		return array();
	}

	/**
	 * get method of current request
	 * @return string
	 */
	public function method()
	{
		return 'GET';
	}

	/**
	 * is get method
	 * @return TRUE/FALSE
	 */
	public function isGet()
	{
		return $this -> method() == 'GET';
	}

	/**
	 * return post
	 * @return TRUE/FALSE
	 */
	public function isPost()
	{
		return $this -> method() == 'POST';
	}

	/**
	 * execute by handler
	 */
	public function execute()
	{
		$controller = $this -> getController();

		Router::sanitizeControllerClass($controller, $class);

		if ($class)
		{

			// explode controller name
			$object = new $class;

			// execute request
			$object -> execute($this);

			unset($ojbect);
		}
		else
		{
			// throw new Exception('Can not excecute requeset without
			// controller.'. $this->_controller);
		}
		return $this;
	}

	/**
	 * instance a new request
	 * @return Request
	 */
	static public function factory($controller, $params = null)
	{
		return new self($controller, $params);
	}

	static function getCurrentUrl()
	{
		return Agilet::getUrl($path = self::initiator() -> getUri(), null);
	}

}
